/**
 * @file STL_Linux.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-10-19
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

#include "STL_Linux.h"
#include <regex>

/*
socket编程中用到的头文件

wlgoc 2016-08-10 15:21:40  8664  收藏 7
socket编程中需要用到的头文件
sys/types.h：数据类型定义

sys/socket.h：提供socket函数及数据结构

netinet/in.h：定义数据结构sockaddr_in

arpa/inet.h：提供IP地址转换函数

netdb.h：提供设置及获取域名的函数

sys/ioctl.h：提供对I/O控制的函数

sys/poll.h：提供socket等待测试机制的函数

 
其他在网络程序中常见的头文件
unistd.h：提供通用的文件、目录、程序及进程操作的函数

errno.h：提供错误号errno的定义,用于错误处理

fcntl.h：提供对文件控制的函数

time.h：提供有关时间的函数

crypt.h：提供使用DES加密算法的加密函数

pwd.h：提供对/etc/passwd文件访问的函数

shadow.h：提供对/etc/shadow文件访问的函数

pthread.h：提供多线程操作的函数

signal.h：提供对信号操作的函数

sys/wait.h、sys/ipc.h、sys/shm.h：提供进程等待、进程间通讯（IPC）及共享内存的函数

 

建议：在编写网络程序时,可以直接使用下面这段头文件代码

#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netdb.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>
#include<malloc.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/ioctl.h>
#include<stdarg.h>
#include<fcntl.h>
#include<fcntl.h>
 

 

涉及到用户权限及密码验证问题时加入如下语句：
#include<shadow.h>
#include<crypt.h>
#include<pwd.h>
需要注意的是,应该在编译时链接加密算法库,即增加编译选项：
-lcrypt

　 

 

涉及到文件及时间操作加入如下语句：
#include<sys/time.h>
#include<utime.h>
#include<time.h>
#include<sys/stat.h>
#include<sys/file.h>
 

 

涉及到多进程操作时加入如下语句：
#include<sys/wait.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<signal.h>
 

 

涉及到多线程操作时加入如下语句：
#include<pthread.h>
#include<sys/poll.h>
需要注意的是,应该在编译时链接线程库,即增加编译选项：
-lthread

*/

#include <stdio.h>
#include <sys/socket.h> // socket()
#include <string.h>     // memset()  strcpy()
#include <net/if.h>     // struct ifreq
#include <errno.h>      // errno
#include <sys/ioctl.h>  // ioctl()
#include <arpa/inet.h>  // inet_ntoa()

#include <stdlib.h>
#include <iostream>
#include <unistd.h>
#include "STLOG.h"

// #include<netinet/in.h>
// #include<sys/types.h>
// #include<net/if_arp.h>

// #include<unistd.h>
// #include<netdb.h>
// #include<stdlib.h>
// #include<ctype.h>
// #include<malloc.h>
// #include<stdarg.h>
// #include<fcntl.h>

using namespace std;

#define STL_LINUX_CAMMEND_RES_LINE_MAX 1024

namespace STL
{
    void stl_daemonlock()
    {
        stlog_debug("stl_daemonlock");
        pid_t pid = getpid();
        stlog_debug("pid:%d", pid);

        char cwd[1024] = {0};
        if (getcwd(cwd, 1024) == NULL)
        {
            stlog_error("cwd get error");
            exit(-1);
        }
        // if (readlink("/proc/self/cwd", cwd, 1024) < 0 || strlen(cwd) >= 1024)
        // {
        //     stlog_error("cwd get error");
        //     exit(-1);
        // }
        stlog_debug("cwd:%s", cwd);

        std::string user = std::string(getlogin());
        stlog_debug("user:%s", user.c_str());

        char exe[1024] = {0};
        if (readlink("/proc/self/exe", exe, 1024) < 0 || strlen(exe) >= 1024)
        {
            stlog_error("exe get error");
            exit(-1);
        }
        stlog_debug("exe:%s", exe);

        // gid_t getegid(void);
        // uid_t geteuid(void);
        // gid_t getgid(void);
        // int getgroups(int, gid_t[]);
        // long gethostid(void);
        // int gethostname(char *, size_t);
        // int getlogin_r(char *, size_t);
        // int getopt(int, char *const[], const char *);
        // pid_t getpgid(pid_t);
        // pid_t getpgrp(void);
        // pid_t getpid(void);
        // pid_t getppid(void);
        // pid_t getsid(pid_t);
        // uid_t getuid(void);

        exit(-1);
    }

    void get_memoccupy(MEM_OCCUPY *mem) //对无类型get函数含有一个形参结构体类弄的指针O
    {
        FILE *fd;
        char buff[256];
        MEM_OCCUPY *m;
        m = mem;

        fd = fopen("/proc/meminfo", "r");
        // # cat /proc/meminfo
        // MemTotal:       32829260 kB
        // MemFree:         3844112 kB
        // MemAvailable:    6127700 kB
        // Buffers:          300144 kB
        // Cached:          2420600 kB
        // SwapCached:        43288 kB

        //从fd文件中读取长度为buff的字符串再存到起始地址为buff这个空间里
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu ", m->MemTotalName, &m->MemTotal);
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu ", m->MemFreeName, &m->MemFree);
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu ", m->MemAvailableName, &m->MemAvailable);
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu ", m->BuffersName, &m->Buffers);
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu ", m->CachedName, &m->Cached);
        fgets(buff, sizeof(buff), fd);
        sscanf(buff, "%s %lu", m->SwapCachedName, &m->SwapCached);

        fclose(fd); //关闭文件fd
    }

    int get_cpuoccupy(CPU_OCCUPY *cpust) //对无类型get函数含有一个形参结构体类弄的指针O
    {
        FILE *fd;
        char buff[256];
        CPU_OCCUPY *cpu_occupy;
        cpu_occupy = cpust;

        fd = fopen("/proc/stat", "r");
        fgets(buff, sizeof(buff), fd);

        sscanf(buff, "%s %u %u %u %u %u %u %u", cpu_occupy->name, &cpu_occupy->user, &cpu_occupy->nice, &cpu_occupy->system, &cpu_occupy->idle, &cpu_occupy->lowait, &cpu_occupy->irq, &cpu_occupy->softirq);

        fclose(fd);

        return 0;
    }

    int uptime(CPU_OCCUPY *cpust) //对无类型get函数含有一个形参结构体类弄的指针O
    {
        FILE *fd;
        char buff[256];
        CPU_OCCUPY *cpu_occupy;
        cpu_occupy = cpust;

        fd = fopen("uptime", "r");
        fgets(buff, sizeof(buff), fd);

        sscanf(buff, "%s %u %u %u %u %u %u %u", cpu_occupy->name, &cpu_occupy->user, &cpu_occupy->nice, &cpu_occupy->system, &cpu_occupy->idle, &cpu_occupy->lowait, &cpu_occupy->irq, &cpu_occupy->softirq);

        fclose(fd);

        return 0;
    }

    void cal_cpuoccupy(CPU_OCCUPY *o, CPU_OCCUPY *n)
    {
        unsigned long od, nd;
        double cpu_use = 0;

        od = (unsigned long)(o->user + o->nice + o->system + o->idle + o->lowait + o->irq + o->softirq); //第一次(用户+优先级+系统+空闲)的时间再赋给od
        nd = (unsigned long)(n->user + n->nice + n->system + n->idle + n->lowait + n->irq + n->softirq); //第二次(用户+优先级+系统+空闲)的时间再赋给od
        double sum = nd - od;
        double idle = n->idle - o->idle;
        cpu_use = idle / sum;
        idle = n->user + n->system + n->nice - o->user - o->system - o->nice;
        cpu_use = idle / sum;
        printf("%.3f\n", cpu_use);
    }

    std::string getCwd()
    {
        char cwd[512] = {0};
        getcwd(cwd, 512);
        return std::string(cwd);
    }

    std::string exec_cmd(const std::string &linuxCmd)
    {
        auto fp = popen(linuxCmd.c_str(), "r");
        if (fp == NULL)
        {
            return "";
        }
        char buf[STL_LINUX_CAMMEND_RES_LINE_MAX];
        stringstream ss1;
        while (fgets(buf, STL_LINUX_CAMMEND_RES_LINE_MAX, fp) != NULL)
        {
            ss1 << string(buf); // 每一行之后都带有换行符\n
        }
        if (pclose(fp) == -1)
        {
            return "";
        }
        return ss1.str();
    }

    int get_cpu_count()
    {
        std::string res = exec_cmd("cat /proc/cpuinfo | grep processor | wc -l");
        return std::stoi(res);
    }

    int get_top_parse_info(System_Info *info)
    {
        // $ top -b -n 1 | head -n 5
        std::string res = exec_cmd("top -b -n 1 | head -n 5");

        // top - 14:39:38 up 70 days, 18:54,  5 users,  load average: 0.81, 0.86, 0.89
        // Tasks: 3335 total,   2 running, 3333 sleeping,   0 stopped,   0 zombie
        // %Cpu(s):  0.7 us,  0.6 sy,  0.0 ni, 98.6 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
        // KiB Mem : 32829260 total,  3813188 free, 25661012 used,  3355060 buff/cache
        // KiB Swap: 16551932 total, 15767016 free,   784916 used.  6109888 avail Mem
        std::regex reg_uptime1("top +- +(\\d{1,2}:\\d{1,2}:\\d{1,2}) +up +(\\d+) days{0,1}, +(\\d{1,2}):(\\d{1,2}), +(\\d+) users{0,1}, +load average: +([\\.\\d]+), +([\\.\\d]+), +([\\.\\d]+)");

        // top - 20:23:12 up 71 days, 38 min,  5 users,  load average: 1.50, 1.48, 1.39
        // Tasks: 3348 total,   1 running, 3347 sleeping,   0 stopped,   0 zombie
        // %Cpu(s):  0.7 us,  0.6 sy,  0.0 ni, 98.6 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
        // KiB Mem : 32829260 total,  4231000 free, 26349968 used,  2248292 buff/cache
        // KiB Swap: 16551932 total, 15572016 free,   979916 used.  5466860 avail Mem
        std::regex reg_uptime2("top +- +(\\d{1,2}:\\d{1,2}:\\d{1,2}) +up +(\\d+) days{0,1}, +(\\d{1,2}) +(min), +(\\d+) users{0,1}, +load average: +([\\.\\d]+), +([\\.\\d]+), +([\\.\\d]+)");

        // top - 18:19:38 up 22:51,  1 user,  load average: 0.02, 0.02, 0.01
        // Tasks: 280 total,   1 running, 279 sleeping,   0 stopped,   0 zombie
        // %Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
        // KiB Mem : 65399220 total, 61028180 free,  1111900 used,  3259140 buff/cache
        // KiB Swap:   999420 total,   999420 free,        0 used. 63590356 avail Mem
        std::regex reg_uptime3("top +- +(\\d{1,2}:\\d{1,2}:\\d{1,2}) +up +(\\d{1,2}):(\\d{1,2}), +(\\d+) users{0,1}, +load average: +([\\.\\d]+), +([\\.\\d]+), +([\\.\\d]+)");

        // top - 18:28:44 up 1 min,  1 user,  load average: 0.52, 0.17, 0.06
        // Tasks: 249 total,   1 running, 248 sleeping,   0 stopped,   0 zombie
        // %Cpu(s):  2.0 us,  0.8 sy,  0.0 ni, 65.4 id, 31.5 wa,  0.0 hi,  0.3 si,  0.0 st
        // KiB Mem :  8174692 total,  7356188 free,   438120 used,   380384 buff/cache
        // KiB Swap:  4190204 total,  4190204 free,        0 used.  7456552 avail Mem
        std::regex reg_uptime4("top +- +(\\d{1,2}:\\d{1,2}:\\d{1,2}) +up +(\\d{1,2}) +(min), +(\\d+) users{0,1}, +load average: +([\\.\\d]+), +([\\.\\d]+), +([\\.\\d]+)");

        std::smatch sm_uptime;
        // same as :
        // std::match_results<std::string::const_iterator> sm;
        if (std::regex_search(res, sm_uptime, reg_uptime1))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_uptime)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_uptime.size(); i++)
            // {
            //     stlog_debug("%s", sm_uptime[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            int day = std::stoi(sm_uptime[2].str());
            int hour = std::stoi(sm_uptime[3].str());
            int minute = std::stoi(sm_uptime[4].str());
            info->uptime = 60 * 60 * 24 * day + 60 * 60 * hour + 60 * minute;
            info->load1 = std::stof(sm_uptime[6].str());
            info->load5 = std::stof(sm_uptime[7].str());
            info->load15 = std::stof(sm_uptime[8].str());
        }
        else if (std::regex_search(res, sm_uptime, reg_uptime2))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_uptime)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_uptime.size(); i++)
            // {
            //     stlog_debug("%s", sm_uptime[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            int day = std::stoi(sm_uptime[2].str());
            int hour = 0;
            int minute = std::stoi(sm_uptime[3].str());
            info->uptime = 60 * 60 * 24 * day + 60 * 60 * hour + 60 * minute;
            info->load1 = std::stof(sm_uptime[6].str());
            info->load5 = std::stof(sm_uptime[7].str());
            info->load15 = std::stof(sm_uptime[8].str());
        }
        else if (std::regex_search(res, sm_uptime, reg_uptime3))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_uptime)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_uptime.size(); i++)
            // {
            //     stlog_debug("%s", sm_uptime[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            int day = 0;
            int hour = std::stoi(sm_uptime[2].str());
            int minute = std::stoi(sm_uptime[3].str());
            info->uptime = 60 * 60 * 24 * day + 60 * 60 * hour + 60 * minute;
            info->load1 = std::stof(sm_uptime[5].str());
            info->load5 = std::stof(sm_uptime[6].str());
            info->load15 = std::stof(sm_uptime[7].str());
        }
        else if (std::regex_search(res, sm_uptime, reg_uptime4))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_uptime)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_uptime.size(); i++)
            // {
            //     stlog_debug("%s", sm_uptime[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            int day = 0;
            int hour = 0;
            int minute = std::stoi(sm_uptime[2].str());
            info->uptime = 60 * 60 * 24 * day + 60 * 60 * hour + 60 * minute;
            info->load1 = std::stof(sm_uptime[5].str());
            info->load5 = std::stof(sm_uptime[6].str());
            info->load15 = std::stof(sm_uptime[7].str());
        }
        else
        {
            stlog_warn("can't macth uptime_load");
            info->uptime = 0;
            info->load1 = 0;
            info->load5 = 0;
            info->load15 = 0;
        }

        std::regex reg_cpus("%Cpu\\(s\\): +([\\.\\d]+) +us, +([\\.\\d]+) +sy, +([\\.\\d]+) +ni, +([\\.\\d]+) +id, +([\\.\\d]+) +wa, +([\\.\\d]+) +hi, +([\\.\\d]+) +si, +([\\.\\d]+) +st");
        // std::regex reg_cpus("Cpu\\(s\\): +([\\.\\d]+) +us,");
        // std::regex reg_cpus("Cpu\\(s\\):");
        std::smatch sm_cpus;
        if (std::regex_search(res, sm_cpus, reg_cpus))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_cpus)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_cpus.size(); i++)
            // {
            //     stlog_debug("%s", sm_cpus[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            info->cpu_percent = 1.f - std::stof(sm_cpus[4].str()) / 100.f;
        }
        else
        {
            stlog_warn("can't macth cpu_percent");
            info->cpu_percent = 0;
        }

        std::regex reg_free("KiB Mem : +([\\.\\d]+) total, +([\\.\\d]+) free, +([\\.\\d]+) used,");
        std::smatch sm_free;
        if (std::regex_search(res, sm_free, reg_free))
        {
            // stlog_debug("-----------------------------");
            // // for (auto x : sm_free)
            // //     std::cout << x << std::endl;
            // for (int i = 0; i < sm_free.size(); i++)
            // {
            //     stlog_debug("%s", sm_free[i].str().c_str());
            // }
            // stlog_debug("-----------------------------");
            info->mem_total = std::stoull(sm_free[1].str());
            info->mem_free = std::stoull(sm_free[2].str());
            info->mem_used = std::stoull(sm_free[3].str());
            info->mem_percent = 1.f * info->mem_used / info->mem_total;
        }
        else
        {
            stlog_warn("can't macth mem_info");
            info->mem_total = 0;
            info->mem_free = 0;
            info->mem_used = 0;
            info->mem_percent = 0;
        }
        return 0;
    }

    int get_mac_addr(const char *name, char *addr)
    {
        struct ifreq s;
        int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

        memset(&s, 0x00, sizeof(struct ifreq));
        strcpy(s.ifr_name, name);
        if (0 == ioctl(fd, SIOCGIFHWADDR, &s))
        {
            int i;
            for (i = 0; i < 6; i++)
                sprintf(addr + i * 2, "%02x", (unsigned char)s.ifr_addr.sa_data[i]);
        }
        else
        {
            return -1;
        }

        return 0;
    }

    int get_local_ip(const char *name, char *ip)
    {
        struct ifreq s;
        int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

        memset(&s, 0x00, sizeof(struct ifreq));
        s.ifr_addr.sa_family = AF_INET;
        strncpy(s.ifr_name, name, IFNAMSIZ - 1);

        if (0 == ioctl(fd, SIOCGIFADDR, &s))
        {
            sprintf(ip, "%s", inet_ntoa(((struct sockaddr_in *)&s.ifr_addr)->sin_addr));
        }
        else
        {
            printf("call ioctl SIOCGIFADDR failed: %s", strerror(errno));
            return -1;
        }

        return 0;
    }
} // namespace STL
